'''
Created on Aug 11, 2009

@author: mjbommar
'''

from tfaces.collection.util import *

try:
    import numpy
    import scipy
    import pylab
    import igraph
except:
    raise Exception("numpy, scipy, pylab, igraph must all be installed")


class Run(object):
    '''
    Runs are objects that store the results of a single run, including
    the parameters used to generate the run and the output resulting from
    the run.  
    
    Ideally, runs are keyed by the random number generator seed, but in their
    absence, a random number is used.
    '''
    
    def __init__(self, parameters, seed = None):
        '''
        The Run constructor takes a dictionary of parameters and outputs.  
        
        Parameter and output keys are assumed to be alphanumeric strings.
        Parameter values are assumed to be either scalar numbers or strings.
        '''

        # Check that the parameter dictionary is OK.
        for (parameter, value) in parameters.iteritems():
            # Make sure the key is a string
            if not isinstance(parameter, str):
                raise Exception("Run::__init__: parameter key %s must all be of type str" % parameter)
            
            if not isOneOf(value, [str, int, float]):
                raise Exception("Run::__init__: parameter % value not all of acceptable type" % parameter)    
        
        self.parameters = parameters
        
        if not seed:
            self.seed = numpy.random.randint(1,2**32)
        else:
            self.seed = seed
        

class TimeSeriesRun(Run):
    '''
    TimeSeriesRun objects store the output of time series experiments.
    '''
    
    def __init__(self, parameters, outputs, seed = None, check=True):
        '''
        The Run constructor takes a dictionary of parameters and outputs.  
        
        Parameter and output keys are assumed to be alphanumeric strings.
        
        Parameter values are assumed to be either scalar numbers or strings.
        Output values are assumed to be one of:
            * Scalar numbers or strings
            * Lists of scalar numbers or strings
        '''
        
        Run.__init__(self, parameters, seed)
        
        self.numRows = 0

        # If check is true, do not assume that the data is clean.
        if check:
            # Check that the output dictionary is OK.
            for (output, value) in outputs.iteritems():
                # Make sure the key is a string
                if not isinstance(output, str):
                    raise Exception("Run::__init__: output key %s must be of type str" % output)
                
                # If the value isn't a scalar number of string, then
                if not isOneOf(value, [str, int, float]):
                    # Is it a list?  Check that the entries are all scalar numbers or strings
                    if isinstance(value, list):
                        if sum(map(lambda v : isOneOf(v, [str, int, float]), value)) != len(value):
                            raise Exception("Run::__init__: output %s value of type list has invalid elements" % output)
                        
                        self.numRows = len(value)
                    else:
                        raise Exception("Run::__init__: output %s value not of supported type" % output)
                else:
                    self.numRows = 1

        # If we made it this far, set the variables.
        self.parameters = parameters
        self.outputs = outputs
    
    def __repr__(self):
        '''
        Return a string representation of the Run object.
        '''
        return "tfaces.collection.TimeSeriesRun: %d parameters, %d outputs, %d rows, seed = %d" % (len(self.parameters), len(self.outputs), self.numRows, self.seed) 


class GraphRun(Run):
    '''
    GraphRun objects store the output of graph experiments.
    '''
    
    def __init__(self, parameters, graph, seed = None, check=True):
        '''
        The Run constructor takes a dictionary of parameters and outputs.  
        
        Parameter and output keys are assumed to be alphanumeric strings.
        
        Parameter values are assumed to be either scalar numbers or strings.
        Output values are assumed to be one of:
            * Scalar numbers or strings
            * Lists of scalar numbers or strings
        '''
        
        Run.__init__(self, parameters, seed)
        
        self.numRows = 0

        # If check is true, do not assume that the data is clean.
        if check:
            if not isinstance(graph, igraph.GraphBase):
                raise Exception("GraphRun::__init__: graph must be of base type igraph.GraphBase")

        # If we made it this far, set the variables.
        self.parameters = parameters
        self.graph = graph
    
    def __repr__(self):
        '''
        Return a string representation of the Run object.
        '''
        return "tfaces.collection.GraphRun: %d parameters, %s, seed = %d" % (len(self.parameters), self.graph, self.seed) 
